﻿#region Copyright © 2011 Prageeth Silva <prageeth@thenewcoders.com>
/*
 * This software is provided 'as-is', without any express or implied warranty.
 * In no event will the author(s) be held liable for any damages arising from
 * the use of this software.
 * 
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 * 
 *   1. The origin of this software must not be misrepresented; you must not
 *      claim that you wrote the original software. If you use this software
 *      in a product, an acknowledgment in the product documentation would be
 *      appreciated but is not required.
 * 
 *   2. Altered source versions must be plainly marked as such, and must not
 *      be misrepresented as being the original software.
 * 
 *   3. This notice may not be removed or altered from any source distribution.
 */
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Drawing;
using System.IO;

namespace SourcePlus.Themes
{

    [XmlRootAttribute]
    public class SourceTheme
    {

        [XmlElement]
        public string Name { get; set; }

        [XmlElement(typeof(Tag))]
        public Tag Comment { get; set; }
       
        [XmlIgnore]
        public Token RtfComment { get; set; }

        [XmlElement(typeof(Tag))]
        public Tag Doc { get; set; }

        [XmlIgnore]
        public Token RtfDoc { get; set; }

        [XmlElement(typeof(Tag))]
        public Tag Keyword { get; set; }

        [XmlIgnore]
        public Token RtfKeyword { get; set; }

        [XmlElement(typeof(Tag))]
        public Tag String { get; set; }

        [XmlIgnore]
        public Token RtfString { get; set; }

        [XmlElement(typeof(Tag))]
        public Tag Operator { get; set; }

        [XmlIgnore]
        public Token RtfOperator { get; set; }

        [XmlElement(typeof(Tag))]
        public Tag PreProcessor { get; set; }

        [XmlIgnore]
        public Token RtfPreProcessor { get; set; }

        [XmlElement(typeof(Tag))]
        public Tag Asp { get; set; }
        
        [XmlIgnore]
        public Token RtfAsp { get; set; }

        [XmlElement(typeof(Tag))]
        public Tag Html { get; set; }

        [XmlIgnore]
        public Token RtfHtml { get; set; }

        [XmlElement(typeof(Tag))]
        public Tag Attribute { get; set; }

        [XmlIgnore]
        public Token RtfAttribute { get; set; }

        [XmlElement(typeof(Tag))]
        public Tag Alernate { get; set; }

        [XmlIgnore]
        public Token RtfAlernate { get; set; }

        [XmlElement(typeof(Tag))]
        public Tag LineNumber { get; set; }

        [XmlIgnore]
        public Token RtfLineNumber { get; set; }

        [XmlIgnore]
        public string Header { get; set; }

        [XmlIgnore]
        public string Footer { get; set; }
        
        [XmlIgnore]
        private Dictionary<string, int> FontTable { get; set; }

        [XmlIgnore]
        private Dictionary<string, int> ColorTable { get; set; }

        public SourceTheme()
        {
            this.FontTable = new Dictionary<string, int>();
            this.ColorTable = new Dictionary<string, int>();
        }

        public bool Initialise()
        {

            // add the tags
            this.AddToTables(this.Comment);
            this.AddToTables(this.Doc);
            this.AddToTables(this.Keyword);
            this.AddToTables(this.String);
            this.AddToTables(this.Operator);
            this.AddToTables(this.PreProcessor);
            this.AddToTables(this.Asp);
            this.AddToTables(this.Html);
            this.AddToTables(this.Attribute);
            this.AddToTables(this.Alernate);
            this.AddToTables(this.LineNumber);
            
            // build font table
            string fonttbl = @"{\fonttbl ";                        
            Dictionary<string, int>.KeyCollection keys = this.FontTable.Keys;
            this.FontTable = new Dictionary<string, int>(keys.Count);
            int i = 0;
            foreach (string s in keys)
            {
                fonttbl += @"{f\" + i + " " + s + ";}";
                this.FontTable.Add(s, i);
                i++;
            }
            fonttbl += "}";

            // build color table
            string clrtbl = @"{\colortbl;";
            keys = this.ColorTable.Keys;
            this.ColorTable = new Dictionary<string, int>(keys.Count);
            i = 1;
            foreach (string s in keys)
            {
                clrtbl += s + ";";
                this.ColorTable.Add(s, i);
                i++;
            }
            clrtbl += "}";

            // convert tags to internal tokens
            this.RtfComment = this.TagToToken(this.Comment);
            this.RtfDoc = this.TagToToken(this.Doc);
            this.RtfKeyword = this.TagToToken(this.Keyword);
            this.RtfString = this.TagToToken(this.String);
            this.RtfOperator = this.TagToToken(this.Operator);
            this.RtfPreProcessor = this.TagToToken(this.PreProcessor);
            this.RtfAsp = this.TagToToken(this.Asp);
            this.RtfHtml = this.TagToToken(this.Html);
            this.RtfAttribute = this.TagToToken(this.Attribute);
            this.RtfAlernate = this.TagToToken(this.Alernate);
            this.RtfLineNumber = this.TagToToken(this.LineNumber);
            
            // build header
            this.Header = @"{\rtf1\ansi\deff0 ";
            this.Header += fonttbl + "\n" + clrtbl + "\n";

            // build footer
            this.Footer = "\n}";

            return true;

        }

        private string HtmlColorToRgb(string html)
        {
            if (html.StartsWith("#")) { html = html.Substring(0); }
            Color c = ColorTranslator.FromHtml(html);
            return @"\red" + c.R.ToString() + @"\green" + c.G.ToString() + @"\blue" + c.B.ToString();
        }

        private void AddToTables(Tag tag)
        {

            if (tag == null) { return; }

            string font = tag.Font.ToLower();
            string color = HtmlColorToRgb(tag.Color);

            if (!this.FontTable.ContainsKey(font)) { this.FontTable.Add(font, 0); }
            if (!this.ColorTable.ContainsKey(color)) { this.ColorTable.Add(color, 0); }
            
        }

        private Token TagToToken(Tag tag)
        {
            if (tag == null) { return null; }
            string before = @"{\f" + this.FontTable[tag.Font.ToLower()] + @"\cf" + this.ColorTable[HtmlColorToRgb(tag.Color)] + tag.Format + " ";
            return new Token(before, "}");
        }

        public bool Save(string file)
        {
            return Save(this, file);
        }

        public override string ToString()
        {
            return this.Name;
        }

        public static SourceTheme LoadFromFile(string file)
        {

            TextReader textReader = null;
            try
            {
                XmlSerializer deserializer = new XmlSerializer(typeof(SourceTheme));
                textReader = new StreamReader(file);
                return (SourceTheme)deserializer.Deserialize(textReader);
            }
            catch
            {
                return null;
            }
            finally
            {
                if (textReader != null) { textReader.Close(); }
            }

        }

        public static bool Save(SourceTheme theme, string file)
        {

            TextWriter textWriter = null;
            try
            {
                XmlSerializer serializer = new XmlSerializer(typeof(SourceTheme));
                textWriter = new StreamWriter(file);
                serializer.Serialize(textWriter, theme);
                textWriter.Close();
                return true;
            }
            catch (Exception e)
            {
                Console.Out.Write(e.StackTrace);
                return false;
            }
            finally
            {
                if (textWriter != null) { textWriter.Close(); }
            }

        }
               
    }

    [XmlRoot]
    public class Tag
    {

        [XmlElement]
        public string Font { get; set; }

        [XmlElement]
        public string Color { get; set; }
        
        [XmlElement]
        public string Format { get; set; }

        private Tag()
        {
            this.Font = string.Empty;
            this.Color = string.Empty;
            this.Format = string.Empty;
        }

        public Tag(string font, string color, string format)
        {
            this.Font = font;
            this.Color = color;
            this.Format = format;
        }

    }
    
    public class Token
    {

        public string Before { get; set; }

        public string After { get; set; }

        public Token(string before, string after)
        {
            this.Before = before;
            this.After = after;
        }

    }

}
